namespace SolidMvvm {
	using System;

	public class Cleaner : IDisposable {
		readonly Exception exceptionOrNull;

		string message = string.Empty;

		/// <summary>
		///     Create this in a 'using' block. If it is resulting from an existing exception, pass the existing exception in so
		///     that the information won't be lost.
		/// </summary>
		public Cleaner(Exception exceptionOrNullParam = null) {
			exceptionOrNull = exceptionOrNullParam;
		}

		/// <summary>
		///     This should be automatically invoked by terminating a 'using' block.
		/// </summary>
		public void Dispose() {
			if (message != string.Empty)
				throw new CleanupException(((exceptionOrNull == null ? string.Empty : exceptionOrNull.Message + "\n") + (message == string.Empty ? string.Empty : "Cleanup error(s): " + message)).Trim(), ((exceptionOrNull == null ? string.Empty : exceptionOrNull + "\n") + (message == string.Empty ? string.Empty : "Cleanup error(s): " + message)).Trim());

			if (exceptionOrNull != null)
				throw new GeneralException(exceptionOrNull.Message, exceptionOrNull.ToString(), exceptionOrNull);
		}

		/// <summary>
		///     Report BOTH the given constructor exception and any additional messages resulting from calling Dispose().
		/// </summary>
		public static void CleanConstructor(IDisposable objectBeingConstructed, Exception constructingException) {
			Exception disposingException = null;
			try {
				objectBeingConstructed.Dispose();
			}
			catch (Exception exception) {
				disposingException = exception;
			}
			throw new ConstructorException(objectBeingConstructed.GetType(), constructingException, disposingException);
		}

		//public static void DisposeObject<T>(ref T disposableObjectOrNull, ref string message) where T : class, IDisposable
		//{
		//    if (disposableObjectOrNull != null)
		//        try
		//        {
		//            disposableObjectOrNull.Dispose();
		//        }
		//        catch (Exception exception)
		//        {
		//            message += exception.Message + "\n";
		//        }
		//    disposableObjectOrNull = null;
		//}

		/// <summary>
		///     Check the given object for null. If it's not null, dispose it, accumulating any exception message, and then set the
		///     object to null.
		/// </summary>
		public void DisposeObject<T>(ref T disposableObjectOrNull) where T : class, IDisposable {
			if (disposableObjectOrNull != null)
				try {
					disposableObjectOrNull.Dispose();
				}
				catch (Exception exception) {
					message += exception.Message + "\n";
				}
			disposableObjectOrNull = null;
		}

		/// <summary>
		///     Check the given object for null. If it's not null, run the lambda, accumulating any exception message, and then set
		///     the object to null.
		/// </summary>
		public void CleanCheckedObject<T>(ref T objectToCheckForNull, Action lambdaExpressionToCleanSomething) where T : class {
			if (objectToCheckForNull != null)
				try {
					lambdaExpressionToCleanSomething();
				}
				catch (Exception exception) {
					message += exception.Message + "\n";
				}
			objectToCheckForNull = null;
		}

		/// <summary>
		///     Run the given lambda, accumulating any exception message.
		/// </summary>
		public void CleanWithLambda(Action lambdaExpressionToCleanSomething) {
			try {
				lambdaExpressionToCleanSomething();
			}
			catch (Exception exception) {
				message += exception.Message + "\n";
			}
		}

		/// <summary>
		///     Simply add an additional error message. This will cause an exception to be thrown when the Cleaner is disposed.
		/// </summary>
		public void AddMessage(string messageParam) {
			message += messageParam + "\n";
		}
	}
}
